package com.weaver.qfengx.sdk.welink;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.weaver.qfengx.*;
import com.weaver.qfengx.sdk.welink.bean.AttendanceRecordsBean;
import com.weaver.qfengx.sdk.welink.bean.WeLinkPageResult;
import com.weaver.qfengx.sdk.welink.bean.WeLinkUserResult;
import com.weaver.qfengx.sdk.welink.config.WeLinkApiConfig;
import com.weaver.qfengx.sdk.welink.param.AttendanceRecordsParam;
import com.weaver.qfengx.sdk.welink.param.TokenParam;
import com.weaver.qfengx.sdk.welink.param.UserDetailParam;
import okhttp3.OkHttpClient;
import okhttp3.Response;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
 * welink sdk封装
 */
public class WeLinkSdk {

    private final static LogUtils log = new LogUtils(WeLinkSdk.class);

    private String clientId;
    private String clientSecret;

    private WeLinkApiConfig weLinkApiConfig;

    private OkHttpClient client;

    // accessToken 缓存
    private Cache<String, String> cache = CacheBuilder.newBuilder()
            .maximumSize(1)
            .expireAfterWrite(6900, TimeUnit.SECONDS)
            .build();

    public WeLinkSdk setWeLinkApiConfig(WeLinkApiConfig weLinkApiConfig) {
        this.weLinkApiConfig = weLinkApiConfig;
        return this;
    }

    public WeLinkSdk(String clientId, String clientSecret, OkHttpClient client) {
        this(clientId, clientSecret, false, new WeLinkApiConfig(), client);
    }

    public WeLinkSdk(String clientId, String clientSecret, WeLinkApiConfig weLinkApiConfig) {
        this(clientId, clientSecret, false, weLinkApiConfig, new OkHttpClient());
    }

    public WeLinkSdk(String clientId, String clientSecret) {
        this(clientId, clientSecret, false, new WeLinkApiConfig(), new OkHttpClient());
    }

    /**
     * 构造 WeLinkSdk
     * @param clinetId appid
     * @param clientSecret appsecret
     * @param isLazy 是否立即获取token
     */
    public WeLinkSdk(String clinetId, String clientSecret, boolean isLazy, WeLinkApiConfig weLinkApiConfig, OkHttpClient client) {
        try {
            this.clientId = clinetId;
            this.clientSecret = clientSecret;
            this.weLinkApiConfig = weLinkApiConfig;
            this.client = client;
            if (!isLazy) { // 直接获取一次token
                accessToken();
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("WeLinkSdk 构建失败：" + e.getMessage());
        }
    }

    public Response requestAccessToken(TokenParam tokenParam) throws IOException {
        Map<String, Object> params = Maps.newHashMap();
        params.put("client_id", tokenParam.getClientId());
        params.put("client_secret", tokenParam.getClientSecret());
        return RequestUtils.doPostByJson(this.client,this.weLinkApiConfig.getAccessTokenUrl(), params);
    }

    private void checkIsOk(JSONObject res) {
        if (!StringUtils.equals("0", res.getString("code"))) {
            throw new RuntimeException("接口返回错误： " + res.toJSONString());
        }
    }

    private Map<String, Object> getTokenMap() throws IOException {
        Response tokenRes = requestAccessToken(new TokenParam()
                .setClientId(this.clientId).setClientSecret(this.clientSecret));
        if (tokenRes.isSuccessful()) {
            JSONObject res = JSON.parseObject(tokenRes.body().string());
            if (StringUtils.equals(res.getString("code"), "0")) {
                String token = res.getString("access_token");
                long expiresTime = res.getLong("expires_in");
                Map<String, Object> resMap = Maps.newHashMap();
                resMap.put("token", token);
                resMap.put("time", expiresTime);
                return resMap;
            } else {
                throw new RuntimeException("获取token请求失败：" + res.getString("message"));
            }
        } else {
            throw new RuntimeException("获取token请求异常: " + tokenRes.body().string());

        }
    }

    /**
     * 获取当前的access_token
     * @return
     */
    public String accessToken() throws ExecutionException, IOException {
        if (Objects.isNull(cache)) { // 如果是第一次，则构建缓存
            Map<String, Object> resMap = getTokenMap();
            this.cache = CacheBuilder.newBuilder()
                    .maximumSize(1)
                    .expireAfterWrite(Long.parseLong(Objects.requireNonNull(StringUtils.val(resMap.get("time")))) - 300, TimeUnit.MINUTES)
                    .build();
            this.cache.put("token", Objects.requireNonNull(StringUtils.val(resMap.get("token"))));
        }
        return cache.get("token", () -> StringUtils.val(getTokenMap().get("token")));
    }

    private Object nullDefaultVal(Object val, Object defaultVal) {
        if (Objects.isNull(val)) {
            return defaultVal;
        }
        return val;
    }

    private JSONObject checkAuth(JSONObject resObj, Supplier<JSONObject> supplier) { // 判断认证是否过期了
        if (StringUtils.equals(resObj.getString("errorCode"), "1000")) { // 用户认证过期了
            return supplier.get();
        }
        return resObj;
    }

    /**
     * 获取考勤打开记录
     * @param param
     * @return
     * @throws IOException
     * @throws ExecutionException
     */
    public WeLinkPageResult attendanceRecords(AttendanceRecordsParam param) throws IOException, ExecutionException {
        Map<String, String> headers = Maps.newHashMap();
        headers.put("x-wlk-Authorization", accessToken());
        Map<String, Object> params = Maps.newHashMap();
        params.put("offset", nullDefaultVal(param.getOffset(), 0));
        params.put("userIdList", nullDefaultVal(param.getUserIdList(), Lists.newArrayList()));
        params.put("startTime", DateUtils.datetime(param.getStartTime()));
        params.put("endTime", DateUtils.datetime(param.getEndTime()));
        params.put("limit", nullDefaultVal(param.getLimit(), 100));
        Response response = RequestUtils.doPostByJson(this.client, this.weLinkApiConfig.getAttendanceRecords(), headers, params);
        if (response.isSuccessful()) {
            String responseStr = response.body().string();
//            System.out.println(responseStr);
            JSONObject res = JSON.parseObject(responseStr);
            checkIsOk(res);
//            checkAuth(res, () -> {
//                this.cache.cleanUp();
//                try {
//                    this.getAccessToken();
//                    Response sub =  RequestUtils.doPostByJson(this.client,this.weLinkApiConfig.getAttendanceRecords(), headers, params);
//                    if (sub.isSuccessful()) {
//                        String str = sub.body().string();
//                        System.out.println(str);
//                        return JSON.parseObject(str);
//                    }
//                    throw new RuntimeException("token再次认证后请求异常");
//                } catch (ExecutionException e) {
//                    throw new RuntimeException(e);
//                } catch (IOException e) {
//                    throw new RuntimeException(e);
//                }
//            }); // 再次请求一次
            WeLinkPageResult weLinkPageResult = new WeLinkPageResult();
            weLinkPageResult.setCode(res.getString("code"));
            weLinkPageResult.setMessage(res.getString("message"));
            weLinkPageResult.setLimit(res.getInteger("limit"));
            weLinkPageResult.setOffset(res.getInteger("offset"));
            weLinkPageResult.setTotalCount(res.getInteger("totalCount"));
            JSONArray dataArr = res.getJSONArray("data");
            List<AttendanceRecordsBean> attendanceRecordsBeanList = Lists.newArrayList();
            if (Objects.nonNull(dataArr)) {
                for (int i = 0; i < dataArr.size(); i++) {
                    JSONObject data = dataArr.getJSONObject(i);
                    AttendanceRecordsBean attendanceRecordsBean = new AttendanceRecordsBean();
                    attendanceRecordsBean.setId(data.getString("id"));
                    attendanceRecordsBean.setCorpUserId(data.getString("corpUserId"));
                    attendanceRecordsBean.setUserId(data.getString("userId"));
                    attendanceRecordsBean.setUserNameCn(data.getString("userNameCn"));
                    attendanceRecordsBean.setDeptName(data.getString("deptName"));
                    attendanceRecordsBean.setGroupName(data.getString("groupName"));
                    attendanceRecordsBean.setPunchTime(data.getString("punchTime"));
                    attendanceRecordsBean.setLocation(data.getString("location"));
                    attendanceRecordsBean.setLongitude(data.getString("longitude"));
                    attendanceRecordsBean.setLatitude(data.getString("latitude"));
                    attendanceRecordsBean.setIsRange(data.getString("isRange"));
                    attendanceRecordsBean.setIsField(data.getString("isField"));
                    attendanceRecordsBean.setRemark(data.getString("remark"));
                    attendanceRecordsBeanList.add(attendanceRecordsBean);
                }
            }
            weLinkPageResult.setData(attendanceRecordsBeanList);
            return weLinkPageResult;
        }
        throw new RuntimeException("请求异常: " + response.body().string());
    }

    private List<String> getStrListOfJsonObj(JSONObject jsonObject, String key) {
        List<String> resList = Lists.newArrayList();
        JSONArray strArr = jsonObject.getJSONArray(key);
        if (Objects.isNull(strArr)) return null;
        for (int i = 0; i < strArr.size(); i++) {
            resList.add(strArr.getString(i));
        }
        return resList;
    }

    private Map<String, Integer> getMapSIOfJsonObj(JSONObject jsonObject, String key) {
        Map<String, Integer> res = Maps.newHashMap();
        JSONObject sub = jsonObject.getJSONObject(key);
        if (Objects.isNull(sub)) return null;
        sub.keySet().forEach(x -> {
            res.put("x", jsonObject.getInteger(x));
        });
        return res;
    }

    private Map<String, String> getMapSSOfJsonObj(JSONObject jsonObject, String key) {
        Map<String, String> res = Maps.newHashMap();
        JSONObject sub = jsonObject.getJSONObject(key);
        if (Objects.isNull(sub)) return null;
        sub.keySet().forEach(x -> {
            res.put("x", jsonObject.getString(x));
        });
        return res;
    }

    /**
     * 获取用户详情
     * @return
     */
    public WeLinkUserResult userDetail(UserDetailParam param) throws IOException, ExecutionException {
        Map<String, String> headers = Maps.newHashMap();
        headers.put("x-wlk-Authorization", accessToken());
        Map<String, Object> params = Maps.newHashMap();
        params.put("userId", nullDefaultVal(param.getUserId(), null));
        params.put("corpUserId", nullDefaultVal(param.getCorpUserId(), null));
        params.put("mobileNumber", nullDefaultVal(param.getMobileNumber(), null));
        Response response = RequestUtils.doPostByJson(this.client, this.weLinkApiConfig.getUserDetailUrl(), headers, params);
        if (response.isSuccessful()) {
            String responseStr = response.body().string();
//            System.out.println(responseStr);
            JSONObject res = JSON.parseObject(responseStr);
            checkIsOk(res);
            WeLinkUserResult weaLinkUserResult = new WeLinkUserResult();
            weaLinkUserResult.setCode(res.getString("code"));
            weaLinkUserResult.setMessage(res.getString("message"));
            weaLinkUserResult.setUserStatus(res.getString("userStatus"));
            weaLinkUserResult.setUserId(res.getString("userId"));
            weaLinkUserResult.setCorpUserId(res.getString("corpUserId"));
            weaLinkUserResult.setUserNameCn(res.getString("userNameCn"));
            weaLinkUserResult.setUserNameEn(res.getString("userNameEn"));
            weaLinkUserResult.setSex(res.getString("sex"));
            weaLinkUserResult.setMobileNumber(res.getString("mobileNumber"));
            weaLinkUserResult.setPhoneNumber(getStrListOfJsonObj(res, "phoneNumber"));
            weaLinkUserResult.setMainCorpDeptCode(res.getString("mainCorpDeptCode"));
            weaLinkUserResult.setMainDeptCode(res.getString("mainDeptCode"));
            weaLinkUserResult.setCorpDeptCodes(getStrListOfJsonObj(res, "corpDeptCodes"));
            weaLinkUserResult.setDeptCodes(getStrListOfJsonObj(res, "deptCodes"));
            weaLinkUserResult.setOrderInDepts(getMapSIOfJsonObj(res, "orderInDepts"));
            weaLinkUserResult.setOrderInCorpDepts(getMapSIOfJsonObj(res, "orderInCorpDepts"));
            weaLinkUserResult.setUserEmail(res.getString("userEmail"));
            weaLinkUserResult.setAvatar(res.getString("avatar"));
            weaLinkUserResult.setEmployeeId(res.getString("employeeId"));
            weaLinkUserResult.setLandlineNumber(res.getString("landlineNumber"));
            weaLinkUserResult.setBusinessAddress(res.getString("businessAddress"));
            weaLinkUserResult.setBaseLocation(res.getString("baseLocation"));
            weaLinkUserResult.setPosition(res.getString("position"));
            weaLinkUserResult.setCorpSecretary(res.getString("corpSecretary"));
            weaLinkUserResult.setSecretary(res.getString("secretary"));
            weaLinkUserResult.setRemark(res.getString("remark"));
            weaLinkUserResult.setIsActivated(res.getInteger("isActivated"));
            weaLinkUserResult.setIsAdmin(res.getInteger("isAdmin"));
            weaLinkUserResult.setSipNum(res.getString("sipNum"));
            weaLinkUserResult.setIsHidePhoneNumber(res.getInteger("isHidePhoneNumber"));
            weaLinkUserResult.setSeniorMode(res.getInteger("seniorMode"));
            weaLinkUserResult.setRoleIds(getStrListOfJsonObj(res, "roleIds"));
            weaLinkUserResult.setExtAttr(getMapSSOfJsonObj(res, "extAttr"));
            weaLinkUserResult.setCreationTime(res.getDate("creationTime"));
            weaLinkUserResult.setLastUpdatedTime(res.getDate("lastUpdatedTime"));
            return weaLinkUserResult;
        }
        throw new RuntimeException("请求异常: " + response.body().string());
    }

    public static void main(String[] args) throws IOException, ExecutionException {
        WeLinkSdk weLinkSdk = new WeLinkSdk("", "");
        System.out.println(weLinkSdk.accessToken());
        AttendanceRecordsParam param = new AttendanceRecordsParam();
        param.setStartTime(DateUtils.dayStart());
        param.setEndTime(DateUtils.dayEnd());
        WeLinkPageResult records = weLinkSdk.attendanceRecords(param);
        if (records.getData().isEmpty()) {
            System.out.println("考勤数据为空");
        }
        for (AttendanceRecordsBean datum : records.getData()) {
            System.out.println(datum);
        }
    }

}
